// helper
function $get(query){ return document.querySelector(query); }

// singleton instance
var fontReplacer = null;

// request options store from background script
chrome.extension.sendRequest({method: "getOptionsStore"}, function(response) {  
  // init fontReplacer
  fontReplacer = new FontReplacer(response.data);
});

FontReplacer = (function() {
	// constructor
	function FontReplacer(data) {    	
		
		// inits
		this.options = new OptionsStore().loadJS(data);				
		this.flipHistory = [];
		
		// startup
		this.setupFonts();
		this.startObserver();
		this.bindFlipKey();
	}	
	
	// adds fonts to page
	FontReplacer.prototype.setupFonts = function() {
		// inject stylesheet with font-head into page
		var $styleNode = document.createElement ("link");
		$styleNode.type = "text/css";
		$styleNode.href = chrome.extension.getURL("styles.css");
		document.head.appendChild($styleNode);		
		
		// calculate helper index used for randomizing fonts
		var index = 0;
		for (var i = 0, len = this.options.fonts.length; i < len; i++){
			var font = this.options.fonts[i];
			var chance = parseInt(font.chance);
			if( chance > 0 )				
				font.index = (index += chance);
			else
				font.index = -1;
		}
		this.options.fontsMaxIndex = index;
	}; 
	
	// observes DOM changes on the quiz body
	FontReplacer.prototype.startObserver = function() {
		var self = this;
		var observer = new MutationObserver(function(mutations) {
			// only 1 childList mutation per post is triggered
			// so no verifications will be done on the mutation type and count
			self.tryChangeFonts();
		});
		observer.observe($get('#rquiz_body'), {childList: true});		
	}; 
	
	// wait for "F" key press and calls the flip between default font and changed font
	FontReplacer.prototype.bindFlipKey = function() {
		var self = this;
		document.addEventListener("keypress", function (e) {
			if(e.charCode == 102 || e.charCode == 70){
				self.flipFonts();
			}
		},false);	
	};
	
	// changes fonts, if appliable
	FontReplacer.prototype.tryChangeFonts = function () {
		var textContent = null;
		var index = null;
		// test questionBox
		if(this.options.replaceOnQuestionBox){
			var $questionbox = $get('#questionbox');
			if ($questionbox) {
				textContent = this.checkTextContent($questionbox.textContent.trim());
				if (textContent.nihongoOnly)
					index = this.changeFont($questionbox, textContent, index);
				else
					$questionbox.style.fontFamily = null;
			}
		}
			
		// test answers
		if(this.options.replaceOnAnswers){
			var $answers = document.querySelectorAll('#ans span.lnk');
			for (var i = 0, len = Math.min($answers.length,this.options.replaceOnAnswersMax); i < len; i++){
				var $answer = $answers[i];
				textContent = this.checkTextContent($answer.textContent);
				if (textContent.nihongoOnly)
					index = this.changeFont($answer, textContent, index);
			}
		}
	
		// reset history
		this.flipHistory.length = 0;
	};
	
	// chooses a random font and applies it
	FontReplacer.prototype.changeFont = function($element,textContent,index){	
		if (!this.options.randomizeOncePerQuestion || !index )
			index = Math.floor((Math.random() * this.options.fontsMaxIndex) + 1);
		for (var i = 0, len = this.options.fonts.length; i < len; i++){
			var font = this.options.fonts[i];
			if (font.index > 0 && font.index >= index && (!font.kanjiOnly || font.kanjiOnly && textContent.kanjiOnly)) {
				$element.style.fontFamily = font.family;
				return index;
			}
		}
		return index;
	};
	
	// flips between default font and changed font
	FontReplacer.prototype.flipFonts = function(){	
		if(this.flipHistory.length > 0){
			for (var i = 0, len = this.flipHistory.length; i < len; i++){
				var data = this.flipHistory[i];
				data.element.style.fontFamily = data.fontFamily;
			}
			this.flipHistory.length = 0;
		} else {
			this.restoreDefaultFont($get('#questionbox'));
			var $answers = document.querySelectorAll('#ans span.lnk');
			for (var i = 0, len = $answers.length; i < len; i++){
				this.restoreDefaultFont($answers[i]);
			}
		}
	};
	
	// adds the changed font to the flip history and restores the default font
	FontReplacer.prototype.restoreDefaultFont = function($element){	
		var fontFamily = $element.style.fontFamily;
		if( fontFamily != undefined ) {
			this.flipHistory.push({element:$element,fontFamily:fontFamily});
			$element.style.fontFamily = null;
		}	
	};

	// Checks if the text contains japanese text and if its composed of only kanji or hira/kana
	// source: http://www.rikai.com/library/kanjitables/kanji_codes.unicode.shtml
	// Japanese-style punctuation ( 3000/12288 - 303f/12351) 	
	// Hiragana ( 3040/12352 - 309f/12447)
	// Katakana ( 30a0/12448 - 30ff/12543)	
	// Full-width roman characters and half-width katakana ( ff00/65280 - ffef/65519)
	// CJK unifed ideographs - Common and uncommon kanji ( 4e00/19968 - 9faf/40879)	
	FontReplacer.prototype.checkTextContent = function (str) {
		var result = {
			kanjiOnly:true,
			hiraOrkanaOnly:true,
			nihongoOnly: true
		};		
		for (var i = 0, len = str.length; i < len; i++) {
			var code = str.charCodeAt(i);
			// exit once '(' is found, avoids wrong detection (ex: when する is present on notes)
			if (code == 40)
			{
				// avoids wrong detection when '(' is at the start, ex: "(public) park"
				if (i == 0)
					result.nihongoOnly = false;
				return result;
			}				

			// ignore punctuation ' ', '~'
			if (!(code >= 12288 && code <= 12351) && code > 32 && code != 126)
			{
				// check hira/kana
				if (code >= 12352 && code <= 12543)
					result.kanjiOnly = false;
					// check kanji
				else if ((code >= 65280 && code <= 65519) || (code >= 19968 && code <= 40879))
					result.hiraOrkanaOnly = false;
				else
					result.nihongoOnly = false;
			}
		}	
		return result;
	};
		
	return FontReplacer;
})();